package kubernetes

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/golang/glog"
	"github.com/gorilla/mux"

	authapi "eccgateway/pkg/api/auth"
	endedgecloud "eccgateway/pkg/api/endedgecloud/v1"
	"eccgateway/pkg/util"
)

type ClusterController struct{}

func (c ClusterController) ListCluster(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}

	q := r.URL.Query()
	labelSelectors := parseLabelSelector(q.Get(labelSelector))

	delete(labelSelectors, groupIDKey)
	delete(labelSelectors, userIDKey)
	largestRole := authapi.GetLargestRolePermission(info.Role)
	switch largestRole {
	case authapi.OpServiceRole:
		break
	case authapi.AdminRole:
		fallthrough	
	case authapi.UserRole:
		fallthrough
	default:
		labelSelectors[groupIDKey] = fmt.Sprintf("%v", info.Group.ID)
	}

	if len(labelSelectors) != 0 {
		q.Del(labelSelector)
		q.Add(labelSelector, constructLabelSelector(labelSelectors))
		r.URL.RawQuery = q.Encode()
	}

	(&KubernetesController{}).ServeHTTP(w, r)
}

func (c ClusterController) CreateCluster(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}

	largestRole := authapi.GetLargestRolePermission(info.Role)
	if largestRole == authapi.UserRole {
		glog.Errorf("ordinary users are not allowed to create cluster, user: %v/%v", info.Name, info.UserID)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, "ordinary users are not allowed to create cluster")
		return
	}

	requestBody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		glog.Errorf("request body read failed, %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, fmt.Sprintf("request body read failed, %v", err))
		return
	}

	cluster := endedgecloud.Cluster{}
	err = json.Unmarshal(requestBody, &cluster)
	if err != nil {
		glog.Errorf("unmarshal request body failed, %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, fmt.Sprintf("unmarshal request body failed, %v", err))
		return
	}

	if cluster.Annotations == nil {
		cluster.Annotations = make(map[string]string)
	}
	cluster.Annotations[creatorAnnotationKey] = fmt.Sprintf("%v", info.Name)
	if cluster.Labels == nil {
		cluster.Labels = make(map[string]string)
	}
	cluster.Labels[userIDKey] = fmt.Sprintf("%v", info.UserID)
	cluster.Labels[userNameKey] = fmt.Sprintf("%v", info.Name)
	cluster.Labels[groupIDKey] = fmt.Sprintf("%v", info.Group.ID)
	cluster.Labels[clusterTypeKey] = cluster.Spec.Type
	newRequestBody, _ := json.Marshal(cluster)

	req, err := http.NewRequest(r.Method, r.URL.String(), bytes.NewBuffer(newRequestBody))
	if err != nil {
		glog.Errorf("construct request failed, %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("construct request failed, %v", err))
		return
	}
	req.RequestURI = r.RequestURI

	(&KubernetesController{}).ServeHTTP(w, req)
}

func (c ClusterController) GetCluster(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}
	largestRole := authapi.GetLargestRolePermission(info.Role)

	clusterName := mux.Vars(r)["name"]
	cluster, err := util.GetCluster(clusterName)
	if err == util.NotFoundError {
		glog.Errorf("get cluster from kubernetes failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusNotFound, err.Error())
		return
	}
	if err != nil {
		glog.Errorf("get cluster from kubernetes failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, err.Error())
		return
	}

	if !(cluster.Labels[groupIDKey] == strconv.Itoa(info.Group.ID) || largestRole == authapi.OpServiceRole) {
		errorMessage := fmt.Sprintf("no permission to get cluster %v", clusterName)
		glog.Errorf("%v, cluster group: %v, creator: %v/%v, request user: %v/%v, user group: %v, user role: %v", errorMessage,
			cluster.Labels[groupIDKey], cluster.Labels[userNameKey], cluster.Labels[userIDKey],
			info.Name, info.UserID, info.Group.ID, largestRole)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, errorMessage)
		return
	}

	respBody, _ := json.Marshal(cluster)
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(respBody)
}

func (c ClusterController) DeleteCluster(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}
	largestRole := authapi.GetLargestRolePermission(info.Role)
	if largestRole == authapi.UserRole {
		errorMessage := "ordinary users are not allowed to delete cluster"
		glog.Errorf(errorMessage)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, errorMessage)
		return
	}

	clusterName := mux.Vars(r)["name"]
	if largestRole == authapi.OpServiceRole {
		glog.Infof("start delete cluster, cluster name: %v, user: %v/%v, user role: %v", clusterName, info.Name, info.UserID, largestRole)
		(&KubernetesController{}).ServeHTTP(w, r)
		return
	}

	cluster, err := util.GetCluster(clusterName)
	if err == util.NotFoundError {
		glog.Errorf("get cluster from kubernetes failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusNotFound, err.Error())
		return
	}
	if err != nil {
		glog.Errorf("get cluster from kubernetes failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, err.Error())
		return
	}
	if cluster.Labels[groupIDKey] == strconv.Itoa(info.Group.ID) && largestRole == authapi.AdminRole {
		glog.Infof("start delete cluster, cluster name: %v, user: %v/%v, user role: %v", clusterName, info.Name, info.UserID, largestRole)
		(&KubernetesController{}).ServeHTTP(w, r)
		return
	}

	errorMessage := fmt.Sprintf("no permission to delete cluster %v", clusterName)
	glog.Errorf("%v, cluster group: %v, creator: %v/%v, request user: %v/%v, user group: %v, user role: %v", errorMessage,
		cluster.Labels[groupIDKey], cluster.Labels[userNameKey], cluster.Labels[userIDKey],
		info.Name, info.UserID, info.Group.ID, largestRole)
	util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, errorMessage)
	return
}

func (c ClusterController) UpdateCluster(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}
	largestRole := authapi.GetLargestRolePermission(info.Role)
	if largestRole == authapi.UserRole {
		errorMessage := "ordinary users are not allowed to update cluster"
		glog.Errorf(errorMessage)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, errorMessage)
		return
	}

	clusterName := mux.Vars(r)["name"]
	if largestRole == authapi.OpServiceRole {
		glog.Infof("start update cluster, cluster name: %v, user: %v/%v, user role: %v", clusterName, info.Name, info.UserID, largestRole)
		(&KubernetesController{}).ServeHTTP(w, r)
		return
	}
	cluster, err := util.GetCluster(clusterName)
	if err == util.NotFoundError {
		glog.Errorf("get cluster from kubernetes failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusNotFound, err.Error())
		return
	}
	if err != nil {
		glog.Errorf("get cluster from kubernetes failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, err.Error())
		return
	}
	r.Header.Set("Content-Type", "application/merge-patch+json")
	if cluster.Labels[groupIDKey] == strconv.Itoa(info.Group.ID) && largestRole == authapi.AdminRole {
		glog.Infof("start update cluster, cluster name: %v, user: %v/%v, user role: %v", clusterName, info.Name, info.UserID, largestRole)
		(&KubernetesController{}).ServeHTTP(w, r)
		return
	}

	errorMessage := fmt.Sprintf("no permission to update cluster %v", clusterName)
	glog.Errorf("%v, cluster group: %v, creator: %v/%v, request user: %v/%v, user group: %v, user role: %v", errorMessage,
		cluster.Labels[groupIDKey], cluster.Labels[userNameKey], cluster.Labels[userIDKey],
		info.Name, info.UserID, info.Group.ID, largestRole)
	util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, errorMessage)
	return
}
